package com.freedom.sql.jdbc.response;

import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import com.freedom.sql.jdbc.netty.MyNioSocketChannel;
import com.freedom.sql.jdbc.utils.AcceptIdUtils;
import com.freedom.sql.jdbc.utils.BufferAllocateUtils;
import com.freedom.sql.jdbc.utils.ByteUtils;
import com.freedom.sql.jdbc.utils.CharsetUtils;
import com.freedom.sql.jdbc.utils.LoggerUtils;
import com.freedom.sql.jdbc.utils.MyConstants;
import com.freedom.sql.jdbc.utils.RandomUtils;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;

public class GreetingPacket {
	private static final Logger logger = LogManager.getLogger(GreetingPacket.class);
	private Channel channel = null;
	@SuppressWarnings("unused")
	private ByteBuf buffer = null;

	public GreetingPacket(Channel c, ByteBuf t) {
		channel = c;
		buffer = t;
	}

	public void write() {
		long begin = System.currentTimeMillis();
		// 1)3字节的长度
		byte[] headerLen = null;
		// 2)packet number:一个字节，值为固定值0
		byte[] packetNumber = MyConstants.PACKET_NUMBER_BYTES;
		// 3)protocol:一个字节，值为10
		byte[] protocol = MyConstants.PROTOCOL_VERSION_BYTES;
		// 4)server version 字符串:以\0结尾,里面可以有自己的一些个性化说明
		byte[] serverDescription = MyConstants.SERVER_VERSION_BYTES;
		// 5)ThreadID:4个字节
		long threadId = AcceptIdUtils.getId();
		byte[] threadIdBytes = ByteUtils.writeLong(threadId, 4);
		// 6)Salt:算上最后的\0是9字节
		byte[] salt1Origin = RandomUtils.randomBytes(8);
		byte[] salt1 = ByteUtils.writeString(new String(salt1Origin));
		// 7)Server Capabilities:2字节
		byte[] serverCapabilities = MyConstants.SERVER_CAPABILITIES_BYTES;
		// 8)Server Language:1字节
		byte[] language = MyConstants.SERVER_LANGUAGE_BYTES;// utf-8
		// 9)Server Status:2字节
		byte[] status = MyConstants.SERVER_STATUS_BYTES;
		// 10)unused: 13个字节
		byte[] unused = new byte[13];
		unused[0] = 0x7f;
		unused[1] = (byte) 0x80;
		unused[2] = 0x15;
		// 11)Salt:算上最后的\0是13字节
		byte[] salt2Origin = RandomUtils.randomBytes(12);
		byte[] salt2 = ByteUtils.writeString(new String(salt2Origin));
		// 12)PayLoad:以\0结尾
		byte[] payLoad = MyConstants.PAYLOAD_BYTES;
		// 现在可以计算总长度了
		int totalPacketLen = 0;
		{
			// totalPacketLen += packetNumber.length;
			totalPacketLen += protocol.length;
			totalPacketLen += serverDescription.length;
			totalPacketLen += threadIdBytes.length;
			totalPacketLen += salt1.length;
			totalPacketLen += serverCapabilities.length;
			totalPacketLen += language.length;
			totalPacketLen += status.length;
			totalPacketLen += unused.length;
			totalPacketLen += salt2.length;
			totalPacketLen += payLoad.length;
			headerLen = ByteUtils.writeInt(totalPacketLen, 3);
		}
		// 构造最终的缓冲区,统一分配,方便后面切换
		ByteBuf finalBuf = BufferAllocateUtils.get(totalPacketLen + 4);
		finalBuf.writeBytes(headerLen);
		finalBuf.writeBytes(packetNumber);
		finalBuf.writeBytes(protocol);
		finalBuf.writeBytes(serverDescription);
		finalBuf.writeBytes(threadIdBytes);
		finalBuf.writeBytes(salt1);
		finalBuf.writeBytes(serverCapabilities);
		finalBuf.writeBytes(language);
		finalBuf.writeBytes(status);
		finalBuf.writeBytes(unused);
		finalBuf.writeBytes(salt2);
		finalBuf.writeBytes(payLoad);
		// 保留一些凭证信息
		{
			MyNioSocketChannel myChannel = (MyNioSocketChannel) channel;
			myChannel.setThreadId(threadId);
			// myChannel.setSalt1(salt1Origin);
			// myChannel.setSalt2(salt2Origin);
			byte[] seed = new byte[salt1Origin.length + salt2Origin.length];
			System.arraycopy(salt1Origin, 0, seed, 0, salt1Origin.length);
			System.arraycopy(salt2Origin, 0, seed, salt1Origin.length, salt2Origin.length);
			myChannel.setSeed(seed);
		}
		// 最后才发送出去,因为不是在selector的线程，实际上是投入到IO队列
		channel.writeAndFlush(finalBuf);
		long end = System.currentTimeMillis();
		LoggerUtils.debug(logger, "send greeting packet cost [" + (end - begin) + "] ms");
		LoggerUtils.debug(logger, "***greeting packet send succeed...");
	}

	// 用于读取响应体///////////////////////////////////////////////////////////
	private Socket socket;

	public GreetingPacket(Socket s) {
		this.socket = s;
	}

	public String read() throws IOException {// 开始读取这几个字段
		// LoggerUtils.debug(logger, "begin to read response from physical
		// mysql");
		InputStream in = socket.getInputStream();
		// 1) 3字节的长度
		byte[] headerBytes = new byte[3];
		in.read(headerBytes, 0, 3);
		int headLength = ByteUtils.littleEndianInteger(headerBytes, 0, 3);
		// LoggerUtils.debug(logger, "length : " + headLength);
		// 2) 1字节的packetNumber
		byte[] packetNumber = new byte[1];
		// LoggerUtils.debug(logger, "try to read packetNumber");
		in.read(packetNumber, 0, 1);
		int packetSeq = ByteUtils.littleEndianInteger(packetNumber, 0, 1);
		// LoggerUtils.debug(logger, "packet Num: " + packetSeq);
		// 一次性读取所有字节
		byte[] allBytes = new byte[headLength];
		in.read(allBytes, 0, headLength);
		// LoggerUtils.debug(logger, "all content is read done!");
		ByteBuf buff = BufferAllocateUtils.get(headLength);
		buff.writeBytes(allBytes);
		// 3)protocol:一个字节，值为10
		int protocol = ByteUtils.readUnsignedByte(buff);
		// LoggerUtils.debug(logger, "protocol: " + protocol);
		// 4)version
		String version = ByteUtils.readZeroTerminatedString(buff);
		// LoggerUtils.debug(logger, "version: " + version);
		// 5)thread Id
		long threadId = ByteUtils.readUnsignedInt(buff, 4);
		// LoggerUtils.debug(logger, "thread Id : " + threadId);
		// 6) salt1
		String salt1 = ByteUtils.readZeroTerminatedString(buff);
		// LoggerUtils.debug(logger, "salt1: " + salt1);
		// 7) serverCapabilities
		int serverCapabilities = ByteUtils.readUnsignedInt(buff, 2);
		// LoggerUtils.debug(logger, "serverCapabilities : 0x" +
		// Integer.toHexString(serverCapabilities));
		// 8)一个字节的语言序号
		int charsetIndex = ByteUtils.readUnsignedByte(buff);
		// LoggerUtils.debug(logger, "charsetIndex : " +
		// CharsetUtils.getCharset(charsetIndex));
		// 9) status,2字节
		int status = ByteUtils.readUnsignedInt(buff, 2);
		// LoggerUtils.debug(logger, "status: 0x" +
		// Integer.toHexString(status));
		// 10)unused 13位
		buff.skipBytes(13);
		// 11) salt1
		String salt2 = ByteUtils.readZeroTerminatedString(buff);
		// LoggerUtils.debug(logger, "salt2: " + salt2);
		// 12)payload-不需要读了,节省时间
		// String payload = ByteUtils.readZeroTerminatedString(buff);
		// LoggerUtils.debug(logger, "payload: " + payload);
		return salt1 + salt2;
	}
}
